#include "DAC8760.h"

void dac8760_init(p_dac8760 dev, uint8_t chan, SPI_HandleTypeDef *spix, GPIO_TypeDef *gpio_cs, uint16_t gpio_pin_cs, GPIO_PinState cs_valid, GPIO_TypeDef *gpio_alarm, uint16_t gpio_pin_alarm, GPIO_PinState alarm_valid)
{
    dev->chan = chan;

    dev->spix = spix;

    dev->gpio_cs = gpio_cs;
    dev->gpio_pin_cs = gpio_pin_cs;
    dev->cs_valid = cs_valid;

    dev->gpio_alarm = gpio_alarm;
    dev->gpio_pin_alarm = gpio_pin_alarm;
    dev->alarm_valid = alarm_valid;
}

int dac8760_rw(p_dac8760 dev, uint8_t cmd, uint16_t txdata)
{
    int tmp;
    uint8_t tx_buf[3], rx_buf[3];

    HAL_GPIO_WritePin(dev->gpio_cs, dev->gpio_pin_cs, dev->cs_valid);

    tx_buf[0] = cmd;
    tx_buf[1] = txdata >> 8;
    tx_buf[2] = txdata & 0xff;

    tmp = HAL_SPI_TransmitReceive(dev->spix, tx_buf, rx_buf, 3, 1000);
    if (tmp == HAL_OK)
    {
        memcpy(dev->in_data, rx_buf, 3);
    }

    HAL_GPIO_WritePin(dev->gpio_cs, dev->gpio_pin_cs, (GPIO_PinState)(!(dev->cs_valid)));

    return tmp;
}

/*!
 * @brief Writes registers on the DAC8760.
 *
 * This function will execute a write register command to the DAC8760. This function can be used to update one register on the DAC8760.
 * No error checking is performed, so it is the user's responsibility to make sure they do not attempt to write past the end of the DAC8760 registers.
 *
 * @param[in]   writeValues    16 bit register value to place in the DAC8760
 * @param[in]   address        Address of the register to write
 *
 * @return  None
 *
 **************************************************************************************************************************************************/
void dac8760_write_reg(p_dac8760 dev, uint8_t addr, uint16_t data)
{
    dac8760_rw(dev, addr, data);
}

/*!
 * @brief Reads registers on the DAC8760.
 *
 * This function will execute a read register command to the DAC8760 and return the resultant data. This function can be used to read one or more
 * registers from the DAC8760. No error checking is performed, so it is the user's responsibility to make sure they do not attempt to read past
 * the end of the DAC8760 registers.
 *
 * @param[out]   *readValues     Pointer to place the 8 bit register values from the DAC8760
 * @param[in]    address         Address of the register to read
 *
 * @return  None
 *
 **************************************************************************************************************************************************/
void dac8760_read_reg(p_dac8760 dev, uint8_t addr, uint8_t *data)
{
    dac8760_rw(dev, DAC8760_READ_REGISTER, addr); //?????

    dac8760_rw(dev, 0, 0); //?
    memcpy(data, dev->in_data, 3);
}

/*!
 * @brief Performs the setup of the DAC8760.
 *
 * This function will configure the DAC8760.
 *
 * @param[in]     controlReg             Value written into DAC8760_CONTROL_REGISTER
 * @param[in]     configurationReg       Value written into DAC8760_CONFIGURATION_REGISTER
 * @param[in]     gainCalReg             Value written into DAC8760_GAIN_CALIBRATION_REGISTER
 * @param[in]     zeroCalReg             Value written into DAC8760_ZERO_CALIBRATION_REGISTER
 *
 * @return  None
 *
 **************************************************************************************************************************************************/
void dac8760_setup(p_dac8760 dev, uint16_t controlReg, uint16_t configurationReg, uint16_t gainCalReg, uint16_t zeroCalReg)
{
    dac8760_reset(dev);
    dac8760_nop(dev);
    dac8760_write_reg(dev, DAC8760_WRITE_CONTROL_REGISTER, controlReg);
    dac8760_write_reg(dev, DAC8760_WRITE_CONFIGURATION_REGISTER, configurationReg);
    dac8760_write_reg(dev, DAC8760_WRITE_GAIN_CALIBRATION_REGISTER, gainCalReg);
    dac8760_write_reg(dev, DAC8760_WRITE_ZERO_CALIBRATION_REGISTER, zeroCalReg);
}

/*!
 * @brief Sends a Reset Command to the DAC8760.
 *
 * This function sends a Reset command to the DAC8760 on the SPI bus.
 *
 * @return  None
 *
 **************************************************************************************************************************************************/
void dac8760_reset(p_dac8760 dev)
{
    dac8760_write_reg(dev, DAC8760_WRITE_RESET_REGISTER, DAC8760_RESET);
}

/*!
 * @brief Sends a Nop Command to the DAC8760.
 *
 * This function sends a Nop command to the DAC8760 on the SPI bus. The DAC8760 will timeout and move into an error state if it does not receive
 * regular commands for the SPI master. This command can be used to notify the DAC8760 that the system is still operational, but no change to the
 * DAC8760 is desired.
 *
 * @return  None
 *
 **************************************************************************************************************************************************/
void dac8760_nop(p_dac8760 dev)
{
    dac8760_write_reg(dev, DAC8760_WRITE_NOP_REGISTER, 0);
}

/*************************************************************************************************************************************************
 *  DAC8760_Set_Out_Value
 **************************************************************************************************************************************************/
/*!
 * @brief Sets the output current from the DAC8760.
 *
 * The DAC8760 is designed to be used in a 4-20mAmp system. Data is communicated by changing the current output from the DAC8760. This function sets
 * the desired output current.
 *
 * @param[in]     value         Value to output from the DAC8760
 *
 * @return  None
 *
 **************************************************************************************************************************************************/
void dac8760_set_out_value(p_dac8760 dev, uint16_t value)
{
    dac8760_write_reg(dev, DAC8760_WRITE_DATA_REGISTER, value);
}

/*************************************************************************************************************************************************
 *  DAC8760_Read_Status
 **************************************************************************************************************************************************/
/*!
 * @brief Reads thE status register from the DAC8760.
 *
 * This function returns the current value in the status register of the DAC8760
 *
 * @return  DAC8760 Status     (DAC8760_FERR_STS, DAC8760_SPI_TIMEOUT_ERR, DAC8760_LOOP_STS, DAC8760_CURR_LOOP_STS)
 **************************************************************************************************************************************************/
uint8_t dac8760_read_status(p_dac8760 dev)
{
    uint8_t data[3];

    dac8760_read_reg(dev, DAC8760_READ_STATUS_REGISTER, data);

    return (data[2]);
}

void test_dac8760(p_dac8760 dev)
{
    uint8_t status=status;
    dac8760_setup(dev, (DAC8760_RANGE_0V_10V + DAC8760_OUTEN), 0, 0, 0); // Config DAC8760
    status = dac8760_read_status(dev);                                   // Read Status DAC8760
    dac8760_set_out_value(dev, 0);                                       // Set DAC8760 output value to 0V
}
